package com.it.data_queue.blocking_queue;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 双锁实现
 *
 * @author: Coke
 * @DateTime: 2023/06/18/13:45
 **/
public class BlockingQueue2<E> implements BlockingQueue<E> {
    private final E[] array;
    private int head;
    private int tail;
    private AtomicInteger size = new AtomicInteger();
    private ReentrantLock headLock = new ReentrantLock();
    private Condition headWaits = headLock.newCondition();
    private ReentrantLock tailLock = new ReentrantLock();
    private Condition tailWaits = tailLock.newCondition();
    
    public BlockingQueue2 (int capacity) {
        array = (E[]) new Object[capacity];
    }
    
    @Override
    public void offer (E e) throws InterruptedException {
        tailLock.lockInterruptibly();
        int c; // 添加前元素的个数
        try {
            while (isPull()) {
                tailWaits.await();
            }
            array[tail] = e;
            if (++ tail == array.length) {
                tail = 0;
            }
            c = size.getAndIncrement();
            if (c + 1 < array.length) {
                tailWaits.signal();
            }
            
        } finally {
            tailLock.unlock();
        }
        
        if (c == 0) {
            headLock.lock();
            try {
                headWaits.signal();
            } finally {
                headLock.unlock();
            }
        }
    }
    
    @Override
    public boolean offer (E e, long timeout) throws InterruptedException {
        return false;
    }
    
    @Override
    public E pool () throws InterruptedException {
        headLock.lockInterruptibly();
        int c; // 取走前元素的个数
        E e;
        try {
            while (isEmpty()) {
                headWaits.await();
            }
            e = array[head];
            array[head] = null;
            if (++ head == array.length) {
                head = 0;
            }
            c = size.getAndDecrement();
            
            if (c > 1) {
                headWaits.signal();
            }
        } finally {
            headLock.unlock();
        }
        
        if (c == array.length) {
            tailLock.lock();
            try {
                tailWaits.signal();
            } finally {
                tailLock.unlock();
            }
        }
        return e;
    }
    
    private boolean isEmpty () {
        return size.get() == 0;
    }
    
    private boolean isPull () {
        return size.get() == array.length;
    }
}
